home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / lib / sunbird / js / calUtils.js < prev    next >
Encoding:
Text File  |  2007-05-23  |  27.9 KB  |  845 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Calendar component utils.
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  *   Joey Minta <jminta@gmail.com>
  18.  * Portions created by the Initial Developer are Copyright (C) 2006
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *
  23.  * Alternatively, the contents of this file may be used under the terms of
  24.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  25.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  26.  * in which case the provisions of the GPL or the LGPL are applicable instead
  27.  * of those above. If you wish to allow use of your version of this file only
  28.  * under the terms of either the GPL or the LGPL, and not to allow others to
  29.  * use your version of this file under the terms of the MPL, indicate your
  30.  * decision by deleting the provisions above and replace them with the notice
  31.  * and other provisions required by the GPL or the LGPL. If you do not delete
  32.  * the provisions above, a recipient may use your version of this file under
  33.  * the terms of any one of the MPL, the GPL or the LGPL.
  34.  *
  35.  * ***** END LICENSE BLOCK ***** */
  36.  
  37. /* This file contains commonly used functions in a centralized place so that
  38.  * various components (and other js scopes) don't need to replicate them. Note
  39.  * that loading this file twice in the same scope will throw errors.
  40.  */
  41.  
  42. const Cc = Components.classes;
  43. const Ci = Components.interfaces;
  44. const Cr = Components.results;
  45. const kSUNBIRD_UID = "{718e30fb-e89b-41dd-9da7-e25a45638b28}";
  46.  
  47. /* Returns a clean new calIEvent */
  48. function createEvent() {
  49.     return Cc["@mozilla.org/calendar/event;1"].createInstance(Ci.calIEvent);
  50. }
  51.  
  52. /* Returns a clean new calITodo */
  53. function createTodo() {
  54.     return Cc["@mozilla.org/calendar/todo;1"].createInstance(Ci.calITodo);
  55. }
  56.  
  57. /* Returns a clean new calIDateTime */
  58. function createDateTime() {
  59.     return Cc["@mozilla.org/calendar/datetime;1"].
  60.            createInstance(Ci.calIDateTime);
  61. }
  62.  
  63. /* Returns a clean new calIRecurrenceInfo */
  64. function createRecurrenceInfo() {
  65.     return Cc["@mozilla.org/calendar/recurrence-info;1"].
  66.            createInstance(Ci.calIRecurrenceInfo);
  67. }
  68.  
  69. /* Returns a clean new calIRecurrenceRule */
  70. function createRecurrenceRule() {
  71.     return Cc["@mozilla.org/calendar/recurrence-rule;1"].
  72.            createInstance(Ci.calIRecurrenceRule);
  73. }
  74.  
  75. /* Returns a clean new calIAttendee */
  76. function createAttendee() {
  77.     return Cc["@mozilla.org/calendar/attendee;1"].
  78.            createInstance(Ci.calIAttendee);
  79. }
  80.  
  81. /* Shortcut to the calendar-manager service */
  82. function getCalendarManager() {
  83.     return Components.classes["@mozilla.org/calendar/manager;1"].
  84.            getService(Ci.calICalendarManager);
  85. }
  86.  
  87. /**
  88.  * Function to get the (cached) best guess at a user's default timezone.  We'll
  89.  * use the value of the calendar.timezone.local preference, if it exists.  If
  90.  * not, we'll do our best guess.
  91.  *
  92.  * @returns  a string of the Mozilla TZID for the user's default timezone.
  93.  */
  94. var gDefaultTimezone;
  95. function calendarDefaultTimezone() {
  96.     if (!gDefaultTimezone) {
  97.         gDefaultTimezone = getPrefSafe("calendar.timezone.local", null);
  98.         if (!gDefaultTimezone) {
  99.             gDefaultTimezone = guessSystemTimezone();
  100.         } else {
  101.             var icsSvc = Cc["@mozilla.org/calendar/ics-service;1"]
  102.                          .getService(Ci.calIICSService);
  103.  
  104.             // Update this tzid if necessary.
  105.             if (icsSvc.latestTzId(gDefaultTimezone).length) {
  106.                 gDefaultTimezone = icsSvc.latestTzId(gDefaultTimezone);
  107.                 setPref("calendar.timezone.local", "CHAR", gDefaultTimezone);
  108.             }
  109.         }
  110.     }
  111.     return gDefaultTimezone;
  112. }
  113.  
  114. /**
  115.  * We're going to do everything in our power, short of rumaging through the
  116.  * user's actual file-system, to figure out the time-zone they're in.  The
  117.  * deciding factors are the offsets given by (northern-hemisphere) summer and
  118.  * winter JSdates.  However, when available, we also use the name of the
  119.  * timezone in the JSdate, or a string-bundle term from the locale.
  120.  *
  121.  * @returns  a ICS timezone string.
  122. */
  123. function guessSystemTimezone() {
  124.     var probableTZ = null;
  125.     var TZname1 = null;
  126.     var TZname2 = null;
  127.     var Date1 = (new Date(2005,6,20)).toString();
  128.     var Date2 = (new Date(2005,12,20)).toString();
  129.     var nameData1 = Date1.match(/[^(]* ([^ ]*) \(([^)]+)\)/);
  130.     var nameData2 = Date2.match(/[^(]* ([^ ]*) \(([^)]+)\)/);
  131.  
  132.     if (nameData1 && nameData1[2]) {
  133.         TZname1 = nameData1[2];
  134.     }
  135.     if (nameData2 && nameData2[2]) {
  136.         TZname2 = nameData2[2];
  137.     }
  138.  
  139.     var index = Date1.indexOf('+');
  140.     if (index < 0) {
  141.         index = Date2.indexOf('-');
  142.     }
  143.  
  144.     // the offset is always 5 characters long
  145.     var TZoffset1 = Date1.substr(index, 5);
  146.     index = Date2.indexOf('+');
  147.     if (index < 0)
  148.         index = Date2.indexOf('-');
  149.     // the offset is always 5 characters long
  150.     var TZoffset2 = Date2.substr(index, 5);
  151.  
  152.     dump("Guessing system timezone:\n");
  153.     dump("TZoffset1: " + TZoffset1 + "\nTZoffset2: " + TZoffset2 + "\n");
  154.     if (TZname1 && TZname2) {
  155.         dump("TZname1: " + TZname1 + "\nTZname2: " + TZname2 + "\n");
  156.     }
  157.  
  158.     var icsSvc = Cc["@mozilla.org/calendar/ics-service;1"].
  159.                  getService(Ci.calIICSService);
  160.  
  161.     // returns 0=definitely not, 1=maybe, 2=likely
  162.     function checkTZ(someTZ)
  163.     {
  164.         var comp = icsSvc.getTimezone(someTZ);
  165.         var subComp = comp.getFirstSubcomponent("VTIMEZONE");
  166.         var standard = subComp.getFirstSubcomponent("STANDARD");
  167.         var standardTZOffset = standard.getFirstProperty("TZOFFSETTO").valueAsIcalString;
  168.         var standardName = standard.getFirstProperty("TZNAME").valueAsIcalString;
  169.         var daylight = subComp.getFirstSubcomponent("DAYLIGHT");
  170.         var daylightTZOffset = null;
  171.         var daylightName = null;
  172.         if (daylight) {
  173.             daylightTZOffset = daylight.getFirstProperty("TZOFFSETTO").valueAsIcalString;
  174.             daylightName = daylight.getFirstProperty("TZNAME").valueAsIcalString;
  175.         }
  176.  
  177.         if (TZoffset2 == standardTZOffset && TZoffset2 == TZoffset1 &&
  178.            !daylight) {
  179.             if (!standardName || standardName == TZname1) {
  180.                 return 2;
  181.             }
  182.             return 1;
  183.         }
  184.  
  185.         if (TZoffset2 == standardTZOffset && TZoffset1 == daylightTZOffset) {
  186.             if ((!standardName || standardName == TZname1) &&
  187.                 (!daylightName || daylightName == TZname2)) {
  188.                 return 2;
  189.             }
  190.             return 1;
  191.         }
  192.  
  193.         // Now flip them and check again, to cover the southern hemisphere case
  194.         if (TZoffset1 == standardTZOffset && TZoffset2 == TZoffset1 &&
  195.            !daylight) {
  196.             if (!standardName || standardName == TZname2) {
  197.                 return 2;
  198.             }
  199.             return 1;
  200.         }
  201.  
  202.         if (TZoffset1 == standardTZOffset && TZoffset2 == daylightTZOffset) {
  203.             if ((!standardName || standardName == TZname2) &&
  204.                 (!daylightName || daylightName == TZname1)) {
  205.                 return 2;
  206.             }
  207.             return 1;
  208.         }
  209.         return 0;
  210.     }
  211.  
  212.     try {
  213.         var stringBundleTZ = calGetString("calendar", "likelyTimezone");
  214.  
  215.         if (stringBundleTZ.indexOf("/mozilla.org/") == -1) {
  216.             // This happens if the l10n team didn't know how to get a time from
  217.             // tzdata.c.  To convert an Olson time to a ics-timezone-string we
  218.             // need to append this prefix.
  219.             // XXX Get this prefix from calIICSService.tzIdPrefix
  220.             stringBundleTZ = "/mozilla.org/20070129_1/" + stringBundleTZ;
  221.         }
  222.  
  223.         switch (checkTZ(stringBundleTZ)) {
  224.             case 0:
  225.                 break;
  226.             case 1:
  227.                 if (!probableTZ)
  228.                     probableTZ = stringBundleTZ;
  229.                 break;
  230.             case 2:
  231.                 return stringBundleTZ;
  232.         }
  233.     }
  234.     catch (ex) { // Oh well, this didn't work, next option...
  235.     }
  236.         
  237.     var tzIDs = icsSvc.timezoneIds;
  238.     while (tzIDs.hasMore()) {
  239.         var theTZ = tzIDs.getNext();
  240.         switch (checkTZ(theTZ)) {
  241.             case 0: break;
  242.             case 1: 
  243.                 if (!probableTZ) {
  244.                     probableTZ = theTZ;
  245.                 }
  246.                 break;
  247.             case 2:
  248.                 return theTZ;
  249.         }
  250.     }
  251.  
  252.     // If we get to this point, should we alert the user?
  253.     if (probableTZ) {
  254.         return probableTZ;
  255.     }
  256.  
  257.     // Everything failed, so this is our only option.
  258.     return "floating";
  259. }
  260.  
  261. /**
  262.  * Shared dialog functions
  263.  */
  264.  
  265. /**
  266.  * Opens the Create Calendar wizard
  267.  *
  268.  * @param aCallback  a function to be performed after calendar creation
  269.  */
  270. function openCalendarWizard(aCallback) {
  271.     openDialog("chrome://calendar/content/calendarCreation.xul", "caEditServer",
  272.                "chrome,titlebar,modal", aCallback);
  273. }
  274.  
  275. /**
  276.  * Opens the calendar properties window for aCalendar
  277.  *
  278.  * @param aCalendar  the calendar whose properties should be displayed
  279.  * @param aCallback  function that should be run when the dialog is accepted
  280.  */
  281. function openCalendarProperties(aCalendar, aCallback) {
  282.     openDialog("chrome://calendar/content/calendarProperties.xul",
  283.                "caEditServer", "chrome,titlebar,modal",
  284.                {calendar: aCalendar, onOk: aCallback});
  285. }
  286.  
  287. /**
  288.  * Opens the print dialog
  289.  */
  290. function calPrint() {
  291.     openDialog("chrome://calendar/content/printDialog.xul", "Print",
  292.                "centerscreen,chrome,resizable");
  293. }
  294.  
  295. /**
  296.  * Other functions
  297.  */
  298.  
  299. /**
  300.  * Takes a string and returns an nsIURI
  301.  *
  302.  * @param aUriString  the string of the address to for the spec of the nsIURI
  303.  *
  304.  * @returns  an nsIURI whose spec is aUriString
  305.  */
  306. function makeURL(aUriString) {
  307.     var ioSvc = Cc["@mozilla.org/network/io-service;1"].
  308.                 getService(Ci.nsIIOService);
  309.     return ioSvc.newURI(aUriString, null, null);
  310. }
  311.  
  312. /**
  313.  * Returns a calIDateTime that corresponds to the current time in the user's
  314.  * default timezone.
  315.  */
  316. function now() {
  317.     var d = createDateTime();
  318.     d.jsDate = new Date();
  319.     return d.getInTimezone(calendarDefaultTimezone());
  320. }
  321.  
  322. /**
  323.  * Returns a calIDateTime corresponding to a javascript Date
  324.  *
  325.  * @param aDate  a javascript date
  326.  * @returns      a calIDateTime whose jsDate is aDate
  327.  *
  328.  * @warning  Use of this function is strongly discouraged.  calIDateTime should
  329.  *           be used directly whenever possible.
  330.  */
  331. function jsDateToDateTime(aDate) {
  332.     var newDate = createDateTime();
  333.     newDate.jsDate = aDate;
  334.     return newDate;
  335. }
  336.  
  337. /**
  338.  * Returns a calIDateTime with a floating timezone and each field the same as
  339.  * the equivalent field of the javascript date aDate
  340.  *
  341.  * @param aDate a javascript date
  342.  * @returns     a calIDateTime with all of the fields the same as aDate
  343.  *
  344.  * @warning  Like jsDateToDateTime, use of the function is strongly discouraged.
  345.  */
  346. function jsDateToFloatingDateTime(aDate) {
  347.     var newDate = createDateTime();
  348.     newDate.timezone = "floating";
  349.     newDate.year = aDate.getFullYear();
  350.     newDate.month = aDate.getMonth();
  351.     newDate.day = aDate.getDate();
  352.     newDate.hour = aDate.getHours();
  353.     newDate.minute = aDate.getMinutes();
  354.     newDate.second = aDate.getSeconds();
  355.     newDate.normalize();
  356.     return newDate;
  357. }
  358.  
  359. /**
  360.  * Selects an item with id aItemId in the radio group with id aRadioGroupId
  361.  *
  362.  * @param aRadioGroupId  the id of the radio group which contains the item
  363.  * @param aItemId        the item to be selected
  364.  */
  365. function calRadioGroupSelectItem(aRadioGroupId, aItemId) {
  366.     var radioGroup = document.getElementById(aRadioGroupId);
  367.     var items = radioGroup.getElementsByTagName("radio");
  368.     var index;
  369.     for (var i in items) {
  370.         if (items[i].getAttribute("id") == aItemId) {
  371.             index = i;
  372.             break;
  373.         }
  374.     }
  375.     ASSERT(index && index != 0, "Can't find radioGroup item to select.", true);
  376.     radioGroup.selectedIndex = index;
  377. }
  378.  
  379. /**
  380.  * Determines whether or not the aObject is a calIEvent
  381.  *
  382.  * @param aObject  the object to test
  383.  * @returns        true if the object is a calIEvent, false otherwise
  384.  */
  385. function isEvent(aObject) {
  386.     return aObject instanceof Ci.calIEvent;
  387. }
  388.  
  389. /**
  390.  * Determines whether or not the aObject is a calITodo
  391.  *
  392.  * @param aObject  the object to test
  393.  * @returns        true if the object is a calITodo, false otherwise
  394.  */
  395. function isToDo(aObject) {
  396.     return aObject instanceof Ci.calITodo;
  397. }
  398.  
  399. /**
  400.  * Normal get*Pref calls will throw if the pref is undefined.  This function
  401.  * will get a bool, int, or string pref.  If the pref is undefined, it will
  402.  * return aDefault.
  403.  *
  404.  * @param aPrefName   the (full) name of preference to get
  405.  * @param aDefault    (optional) the value to return if the pref is undefined
  406.  */
  407. function getPrefSafe(aPrefName, aDefault) {
  408.     const nsIPrefBranch = Components.interfaces.nsIPrefBranch;
  409.     const prefB = Components.classes["@mozilla.org/preferences-service;1"]
  410.                             .getService(nsIPrefBranch);
  411.     // Since bug 193332 does not fix the current branch, calling get*Pref will
  412.     // throw NS_ERROR_UNEXPECTED if clearUserPref() was called and there is no
  413.     // default value. To work around that, catch the exception.
  414.     try {
  415.         switch (prefB.getPrefType(aPrefName)) {
  416.             case nsIPrefBranch.PREF_BOOL:
  417.                 return prefB.getBoolPref(aPrefName);
  418.             case nsIPrefBranch.PREF_INT:
  419.                 return prefB.getIntPref(aPrefName);
  420.             case nsIPrefBranch.PREF_STRING:
  421.                 return prefB.getCharPref(aPrefName);
  422.             default: // includes nsIPrefBranch.PREF_INVALID
  423.                 return aDefault;
  424.         }
  425.     } catch (e) {
  426.         return aDefault;
  427.     }
  428. }
  429.  
  430. /**
  431.  * Wrapper for setting prefs of various types
  432.  *
  433.  * @param aPrefName   the (full) name of preference to set
  434.  * @param aPrefType   the type of preference to set.  Valid valuse are:
  435.                         BOOL, INT, and CHAR
  436.  * @param aPrefValue  the value to set the pref to
  437.  */
  438. function setPref(aPrefName, aPrefType, aPrefValue) {
  439.     const nsIPrefBranch = Components.interfaces.nsIPrefBranch;
  440.     const prefB = Components.classes["@mozilla.org/preferences-service;1"]
  441.                             .getService(nsIPrefBranch);
  442.     switch (aPrefType) {
  443.         case "BOOL":
  444.             prefB.setBoolPref(aPrefName, aPrefValue);
  445.             break;
  446.         case "INT":
  447.             prefB.setIntPref(aPrefName, aPrefValue);
  448.             break;
  449.         case "CHAR":
  450.             prefB.setCharPref(aPrefName, aPrefValue);
  451.             break;
  452.     }
  453. }
  454.  
  455. /**
  456.  * Helper function to set a localized (complex) pref from a given string
  457.  *
  458.  * @param aPrefName   the (full) name of preference to set
  459.  * @param aString     the string to which the preference value should be set
  460.  */
  461. function setLocalizedPref(aPrefName, aString) {
  462.     const prefB = Cc["@mozilla.org/preferences-service;1"].
  463.                   getService(Ci.nsIPrefBranch);
  464.     var str = Cc["@mozilla.org/supports-string;1"].
  465.               createInstance(Ci.nsISupportsString);
  466.     str.data = aString;
  467.     prefB.setComplexValue(aPrefName, Ci.nsISupportsString, str);
  468. }
  469.  
  470. /**
  471.  * Like getPrefSafe, except for complex prefs (those used for localized data).
  472.  *
  473.  * @param aPrefName   the (full) name of preference to get
  474.  * @param aDefault    (optional) the value to return if the pref is undefined
  475.  */
  476. function getLocalizedPref(aPrefName, aDefault) {
  477.     const pb2 = Cc["@mozilla.org/preferences-service;1"].
  478.                 getService(Ci.nsIPrefBranch2);
  479.     var result;
  480.     try {
  481.         result = pb2.getComplexValue(aPrefName, Ci.nsISupportsString).data;
  482.     } catch(ex) {
  483.         return aDefault;
  484.     }
  485.     return result;
  486. }
  487.  
  488. /**
  489.  * Gets the value of a string in a .properties file
  490.  *
  491.  * @param aBundleName  the name of the properties file.  It is assumed that the
  492.  *                     file lives in chrome://calendar/locale/
  493.  * @param aStringName  the name of the string within the properties file
  494.  * @param aParams      optional array of parameters to format the string
  495.  */
  496. function calGetString(aBundleName, aStringName, aParams) {
  497.     var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  498.                         .getService(Components.interfaces.nsIStringBundleService);
  499.     var props = sbs.createBundle("chrome://calendar/locale/"+aBundleName+".properties");
  500.  
  501.     if (aParams && aParams.length) {
  502.         return props.formatStringFromName(aStringName, aParams, aParams.length);
  503.     } else {
  504.         return props.GetStringFromName(aStringName);
  505.     }
  506. }
  507.  
  508. /** Returns a best effort at making a UUID.  If we have the UUIDGenerator
  509.  * service available, we'll use that.  If we're somewhere where it doesn't
  510.  * exist, like Lightning in TB 1.5, we'll just use the current time.
  511.  */
  512. function getUUID() {
  513.     if ("@mozilla.org/uuid-generator;1" in Components.classes) {
  514.         var uuidGen = Cc["@mozilla.org/uuid-generator;1"].
  515.                       getService(Ci.nsIUUIDGenerator);
  516.         // generate uuids without braces to avoid problems with 
  517.         // CalDAV servers that don't support filenames with {}
  518.         return uuidGen.generateUUID().toString().replace(/[{}]/g, '');
  519.     }
  520.     // No uuid service (we're on the 1.8.0 branch)
  521.     return "uuid" + (new Date()).getTime();
  522. }
  523.  
  524. /** Due to a bug in js-wrapping, normal == comparison can fail when we
  525.  * have 2 objects.  Use these functions to force them both to get wrapped
  526.  * the same way, allowing for normal comparison.
  527.  */
  528.  
  529. /**
  530.  * calIItemBase comparer
  531.  */
  532. function compareItems(aItem, aOtherItem) {
  533.     var sip1 = Cc["@mozilla.org/supports-interface-pointer;1"].
  534.                createInstance(Ci.nsISupportsInterfacePointer);
  535.     sip1.data = aItem;
  536.     sip1.dataIID = Ci.calIItemBase;
  537.  
  538.     var sip2 = Cc["@mozilla.org/supports-interface-pointer;1"].
  539.                createInstance(Ci.nsISupportsInterfacePointer);
  540.     sip2.data = aOtherItem;
  541.     sip2.dataIID = Ci.calIItemBase;
  542.     return sip1.data == sip2.data;
  543. }
  544.  
  545. /**
  546.  * Generic object comparer
  547.  * Use to compare two objects which are not of type calIItemBase, in order
  548.  * to avoid the js-wrapping issues mentioned above.
  549.  *
  550.  * @param aObject        first object to be compared
  551.  * @param aOtherObject   second object to be compared
  552.  * @param aIID           IID to use in comparison
  553.  */
  554. function compareObjects(aObject, aOtherObject, aIID) {
  555.     var sip1 = Cc["@mozilla.org/supports-interface-pointer;1"].
  556.                createInstance(Ci.nsISupportsInterfacePointer);
  557.     sip1.data = aObject;
  558.     sip1.dataIID = aIID;
  559.  
  560.     var sip2 = Cc["@mozilla.org/supports-interface-pointer;1"].
  561.                createInstance(Ci.nsISupportsInterfacePointer);
  562.     sip2.data = aOtherObject;
  563.     sip2.dataIID = aIID;
  564.     return sip1.data == sip2.data;
  565. }
  566.  
  567. /**
  568.  * Many computations want to work only with date-times, not with dates.  This
  569.  * method will return a proper datetime (set to midnight) for a date object.  If
  570.  * the object is already a datetime, it will simply be returned.
  571.  *
  572.  * @param aDate  the date or datetime to check
  573.  */
  574. function ensureDateTime(aDate) {
  575.     if (!aDate) {
  576.         return null;
  577.     }
  578.     if (!aDate.isDate) {
  579.         return aDate;
  580.     }
  581.     var newDate = aDate.clone();
  582.     newDate.hour = 0;
  583.     newDate.minute = 0;
  584.     newDate.second = 0;
  585.     newDate.isDate = false;
  586.     return newDate;
  587. }
  588.  
  589. /****
  590.  **** debug code
  591.  ****/
  592.  
  593. /**
  594.  * Logs a string or an object to both stderr and the js-console only in the case 
  595.  * where the calendar.debug.log pref is set to true.
  596.  *
  597.  * @param aArg  either a string to log or an object whose entire set of 
  598.  *              properties should be logged.
  599.  */
  600. function LOG(aArg) {
  601.     var prefB = Cc["@mozilla.org/preferences-service;1"].
  602.                 getService(Ci.nsIPrefBranch);
  603.     var shouldLog = false;
  604.     try {
  605.         shouldLog = prefB.getBoolPref("calendar.debug.log");
  606.     } catch(ex) {}
  607.  
  608.     if (!shouldLog) {
  609.         return;
  610.     }
  611.     ASSERT(aArg, "Bad log argument.", false);
  612.     var string;
  613.     // We should just dump() both String objects, and string primitives.
  614.     if (!(aArg instanceof String) && !(typeof(aArg) == "string")) {
  615.         var string = "Logging object...\n";
  616.         for (var prop in aArg) {
  617.             string += prop + ': ' + aArg[prop] + '\n';
  618.         }
  619.         string += "End object\n";
  620.     } else {
  621.         string = aArg;
  622.     }
  623.  
  624.     dump(string + '\n');
  625.     var consoleSvc = Cc["@mozilla.org/consoleservice;1"].
  626.                      getService(Ci.nsIConsoleService);
  627.     consoleSvc.logStringMessage(string);
  628. }
  629.  
  630. /**
  631.  * Returns a string describing the current js-stack.  Note that this is
  632.  * different than Components.stack, in that STACK just returns that js
  633.  * functions that were called on the way to this function.
  634.  *
  635.  * @param aDepth (optional) The number of frames to include
  636.  */
  637. function STACK(aDepth) {
  638.     var depth = aDepth || 5;
  639.     var stack = "";
  640.     var frame = arguments.callee.caller;
  641.     for (var i = 1; i <= depth; i++) {
  642.         stack += i+": "+ frame.name+ "\n";
  643.         frame = frame.arguments.callee.caller;
  644.         if (!frame) {
  645.             break;
  646.         }
  647.     }
  648.     return stack;
  649. }
  650.  
  651. /**
  652.  * Logs a message and the current js-stack, if aCondition fails
  653.  *
  654.  * @param aCondition  the condition to test for
  655.  * @param aMessage    the message to report in the case the assert fails
  656.  * @param aCritical   if true, throw an error to stop current code execution
  657.  *                    if false, code flow will continue
  658.  */
  659. function ASSERT(aCondition, aMessage, aCritical) {
  660.     if (aCondition) {
  661.         return;
  662.     }
  663.  
  664.     var string = "Assert failed: " + aMessage + '\n' + STACK();
  665.     if (aCritical) {
  666.         throw new Error(string);
  667.     } else {
  668.         Components.utils.reportError(string);
  669.     }
  670. }
  671.  
  672.  
  673. /**
  674.  * Auth prompt implementation - Uses password manager if at all possible.
  675.  */
  676. function calAuthPrompt() {
  677.     // use the window watcher service to get a nsIAuthPrompt impl
  678.     this.mPrompter = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  679.                                .getService(Components.interfaces.nsIWindowWatcher)
  680.                                .getNewAuthPrompter(null);
  681.     this.mTriedStoredPassword = false;
  682. }
  683.  
  684. calAuthPrompt.prototype = {
  685.     prompt: function capP(aDialogTitle, aText, aPasswordRealm, aSavePassword,
  686.                           aDefaultText, aResult) {
  687.         return this.mPrompter.prompt(aDialogTitle, aText, aPasswordRealm,
  688.                                      aSavePassword, aDefaultText, aResult);
  689.     },
  690.  
  691.     getPasswordInfo: function capGPI(aPasswordRealm) {
  692.         var username;
  693.         var password;
  694.         var found = false;
  695.         var passwordManager = Components.classes["@mozilla.org/passwordmanager;1"]
  696.                                         .getService(Components.interfaces.nsIPasswordManager);
  697.         var pwenum = passwordManager.enumerator;
  698.         // step through each password in the password manager until we find the one we want:
  699.         while (pwenum.hasMoreElements()) {
  700.             try {
  701.                 var pass = pwenum.getNext().QueryInterface(Components.interfaces.nsIPassword);
  702.                 if (pass.host == aPasswordRealm) {
  703.                      // found it!
  704.                      username = pass.user;
  705.                      password = pass.password;
  706.                      found = true;
  707.                      break;
  708.                 }
  709.             } catch (ex) {
  710.                 // don't do anything here, ignore the password that could not
  711.                 // be read
  712.             }
  713.         }
  714.         return {found: found, username: username, password: password};
  715.     },
  716.  
  717.     promptUsernameAndPassword: function capPUAP(aDialogTitle, aText,
  718.                                                 aPasswordRealm,aSavePassword,
  719.                                                 aUser, aPwd) {
  720.         var pw;
  721.         if (!this.mTriedStoredPassword) {
  722.             pw = this.getPasswordInfo(aPasswordRealm);
  723.         }
  724.  
  725.         if (pw && pw.found) {
  726.             this.mTriedStoredPassword = true;
  727.             aUser.value = pw.username;
  728.             aPwd.value = pw.password;
  729.             return true;
  730.         } else {
  731.             return this.mPrompter.promptUsernameAndPassword(aDialogTitle, aText,
  732.                                                             aPasswordRealm,
  733.                                                             aSavePassword,
  734.                                                             aUser, aPwd);
  735.         }
  736.     },
  737.  
  738.     // promptAuth is needed/used on trunk only
  739.     promptAuth: function capPA(aChannel, aLevel, aAuthInfo) {
  740.         // need to match the way the password manager stores host/realm
  741.         var hostRealm = aChannel.URI.host + ":" + aChannel.URI.port + " (" +
  742.                         aAuthInfo.realm + ")";
  743.         var pw;
  744.         if (!this.mTriedStoredPassword) {
  745.             pw = this.getPasswordInfo(hostRealm);
  746.         }
  747.  
  748.         if (pw && pw.found) {
  749.             this.mTriedStoredPassword = true;
  750.             aAuthInfo.username = pw.username;
  751.             aAuthInfo.password = pw.password;
  752.             return true;
  753.         } else {
  754.             var prompter2 = 
  755.                 Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  756.                           .getService(Components.interfaces.nsIPromptFactory)
  757.                           .getPrompt(null, Components.interfaces.nsIAuthPrompt2);
  758.             return prompter2.promptAuth(aChannel, aLevel, aAuthInfo);
  759.         }
  760.     },
  761.  
  762.     promptPassword: function capPP(aDialogTitle, aText, aPasswordRealm,
  763.                              aSavePassword, aPwd) {
  764.         var found = false;
  765.         var pw;
  766.         if (!this.mTriedStoredPassword) {
  767.             pw = this.getPasswordInfo(aPasswordRealm);
  768.         }
  769.  
  770.         if (pw && pw.found) {
  771.             this.mTriedStoredPassword = true;
  772.             aPwd.value = pw.password;
  773.             return true;
  774.         } else {
  775.             return this.mPrompter.promptPassword(aDialogTitle, aText,
  776.                                                  aPasswordRealm, aSavePassword,
  777.                                                  aPwd);
  778.         }
  779.     }
  780. }
  781.  
  782. /**
  783.  * Pick whichever of "black" or "white" will look better when used as a text
  784.  * color against a background of bgColor. 
  785.  *
  786.  * @param bgColor   the background color as a "#RRGGBB" string
  787.  */
  788. function getContrastingTextColor(bgColor)
  789. {
  790.     var calcColor = bgColor.replace(/#/g, "");
  791.     var red = parseInt(calcColor.substring(0, 2), 16);
  792.     var green = parseInt(calcColor.substring(2, 4), 16);
  793.     var blue = parseInt(calcColor.substring(4, 6), 16);
  794.  
  795.     // Calculate the brightness (Y) value using the YUV color system.
  796.     var brightness = (0.299 * red) + (0.587 * green) + (0.114 * blue);
  797.  
  798.     // Consider all colors with less than 56% brightness as dark colors and
  799.     // use white as the foreground color, otherwise use black.
  800.     if (brightness < 144) {
  801.         return "white";
  802.     }
  803.  
  804.     return "black";
  805. }
  806.  
  807. /**
  808.  * Returns the start date of an item, ie either an event's start date or a task's entry date.
  809.  */
  810. function calGetStartDate(aItem)
  811. {
  812.     return (isEvent(aItem) ? aItem.startDate : aItem.entryDate);
  813. }
  814.  
  815. /**
  816.  * Returns the end date of an item, ie either an event's end date or a task's due date.
  817.  */
  818. function calGetEndDate(aItem)
  819. {
  820.     return (isEvent(aItem) ? aItem.endDate : aItem.dueDate);
  821. }
  822.  
  823. /**
  824.  *  Delete the current selected items with focus from the unifinder list
  825.  */
  826. function deleteEventCommand(doNotConfirm)
  827. {
  828.     var selectedItems = currentView().getSelectedItems({});
  829.     calendarViewController.deleteOccurrences(selectedItems.length,
  830.                                              selectedItems,
  831.                                              false,
  832.                                              false);
  833. }
  834.  
  835. /**
  836.  * Returns true if we are Sunbird (according to our UUID), false otherwise.
  837.  */
  838. function isSunbird()
  839. {
  840.     var appInfo = Cc["@mozilla.org/xre/app-info;1"].
  841.                   getService(Ci.nsIXULAppInfo);
  842.  
  843.     return appInfo.ID == kSUNBIRD_UID;
  844. }
  845.